<?php
/**
 * Zhub - A PHP Framework For API Developer
 *
 * This file is part of the Zhub package.
 *
 * (c) YiLong Fan <fantiq@163.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @author fanyilong <fantiq@163.com>
 * @copyright 2017 fanyilong. All rights reserved.
 * @package Zhub
 * @date 18-3-4
 */

namespace Zhub\Uri\Url;


use Zhub\Uri\Url\Exceptions\InvalidUrlException;

class UrlHandle
{
    public static function parse(string $urlString, UrlStruct $struct):UrlStruct
    {
        // scheme
        list($scheme, $url) = self::splitUrl('://', $urlString);
        $struct->setScheme($scheme??'');

        // fragment
        list($url, $fragment) = self::splitUrl('#', $url, true);
        $struct->setFragment($fragment??'');

        // query
        list($url, $query) = self::splitUrl('?', $url, true);
        $struct->setQuery($query??'');

        // authority
        list($authority, $url) = self::splitUrl('/', $url, true);
        self::parseAuthority((string)$authority, $struct);

        // path
        $struct->setPath((string)$url);
        return $struct;
    }

    public static function parseAuthority(string $authority, UrlStruct $struct)
    {
        list($username, $host) = self::splitUrl('@', $authority);
        if (!empty($username)) {
            list($username, $password) = self::splitUrl(':', $username, true);
            $struct->setUsername($username);
            $struct->setPassword($password??'');
        }

        list($host, $port) = self::splitUrl(':', $host, true);
        $host = $host ?? null;
        if (empty($host)) {
            throw new InvalidUrlException();
        }
        $struct->setHost($host??'');
        $struct->setPort($port?intval($port):80);
    }

    public static function resolve(UrlStruct $struct):string
    {
        $scheme = $struct->getScheme();
        $scheme = empty($scheme) ? 'http' : $scheme;
        $url = $scheme . '://' . self::resolveAuthority($struct) . $struct->getPath();

        $query = $struct->getQuery();
        if (!empty($query)) {
            $url .= '?' . $query;
        }

        $fragment = $struct->getFragment();
        if (!empty($fragment)) {
            $url .= '#' . $fragment;
        }

        return $url;
    }

    public static function resolveAuthority(UrlStruct $struct)
    {
        $authority = '';
        $username = $struct->getUsername();
        $password = $struct->getPassword();
        if (!empty($username)) {
            $authority .= $username;
            if (!empty($password)) {
                $authority .= ':' . $password;
            }
            $authority .= '@';
        }

        $port = $struct->getPort();
        $host = $struct->getHost();
        if (empty($host)) {
            throw new InvalidUrlException();
        }
        if ($port != 80) {
            $host .= ':' . $port;
        }

        return $authority . $host;
    }

    private static function splitUrl(string $separator, string $url, bool $reverse = false)
    {
        $result = null;
        if (stripos($url, $separator) !== false) {
            $result = explode($separator, $url, 2);
        }
        else {
            $result = [null, $url];
            if ($reverse) {
                $result = array_reverse($result);
            }
        }

        return $result;
    }
}